﻿import copy
import re
from typing import Dict, List

from core import utils
from core.data_read import GatewayCase, NodeCase, TestResult, TestCase, TestDevice, ExportSingle, \
    TestDeviceMul, ExportMulticast
from core.serial_port import SerialPort
import time
from api import config, mqtt
from logger.logger import Log
from enum import Enum


class ActionEnum(Enum):
    Ps = 'Performance_Start'
    Pm = 'Performance_Mid'
    Pe = 'Performance_End'


class AutoTest:
    running = False
    case_name = ''
    temp_data = ''
    case_data = {}
    Start_time_map = {}

    def read_output(self, timeout: int) -> bytes:
        output = self.sp.read(timeout)
        return output

    def exec_cmd(self, cmd: str, timeout: int) -> str:
        self.sp.write(cmd)
        output = self.sp.read(timeout)
        if 'Hex' not in cmd:
            return output.decode('utf-8')

    def gw_cmd_parse(self, gw_cmd_list) -> list:
        cmd_list = []
        if len(gw_cmd_list) != 0:

            for i in gw_cmd_list:
                if 'S:' in i:
                    cmd_list.append(re.findall(r'(?<=S:).+', GatewayCase.CmdMap[i].replace("\n", ""))[0])
                else:
                    cmd_list.append(GatewayCase.CmdMap[i].replace("\n", ""))
        return cmd_list

    def node_cmd_parse(self, node_cmd_list) -> list:
        cmd_list = []
        if len(node_cmd_list) != 0:
            for i in node_cmd_list:
                cmd_list.append(re.findall(r'(?<=S:).+', NodeCase.CmdMap[i])[0])
        return cmd_list

    def test(self, case: TestCase):
        case = copy.copy(case)
        AutoTest.case_name = case.name
        AutoTest.running = True
        self.start_time = time.time()
        gw_cmd_list = self.gw_cmd_parse(case.gw_cmd_list)
        nd_cmd_list = self.node_cmd_parse(case.nd_cmd_list)
        node_dic = {}
        if len(gw_cmd_list) != 0:
            for msg in gw_cmd_list:

                if "@NodeList" in msg:
                    node_list = config.Config.arg['TestCase']['Node_List']
                    count = 0
                    for i in node_list:
                        temp_msg = msg.replace("@NodeList", i)
                        AutoTest.Start_time_map.update({i: time.time()})
                        if "@MacList" in temp_msg:
                            node_mac_list = config.Config.arg['TestCase']['Node_MAC_List']
                            temp_msg = temp_msg.replace("@MacList", node_mac_list[count])
                            count += 1
                        self.server.send_msg(temp_msg)
                        time.sleep(case.delay)

                else:
                    self.server.send_msg(msg)
                    time.sleep(case.delay)

        if len(nd_cmd_list) != 0:
            for cmd in nd_cmd_list:
                log = "[Send Command:] {}".format(cmd)
                Log.logger.info(log)
                readback = self.exec_cmd(cmd, 1000)
                log = "[Receive Command:] {}".format(readback)
                Log.logger.info(log)
                temp_node = {cmd: readback}
                node_dic.update(temp_node)
        time.sleep(case.timeout)
        self.timeout = True
        AutoTest.case_data.update({AutoTest.case_name: AutoTest.temp_data})
        AutoTest.temp_data = ''
        AutoTest.running = False
        self.operation[self.user_case_name](case, ActionEnum.Ps)

    def test_performance(self, case, style):
        if style == ActionEnum.Ps:
            if case.result_info is not None:
                res, msg, end_time, temp_error_list = case.result_info.is_exist(
                    AutoTest.case_data[AutoTest.case_name])
                if temp_error_list is not None:
                    self.result.add_error_case({self.count: temp_error_list})
                if res:
                    self.result.test_pass()
                    self.end_time = utils.timestr_to_timestamp(end_time) if end_time is not None else None
                    if self.end_time is not None:
                        use_time = self.end_time  - self.start_time
                        self.result.add_pass_case(use_time)
                else:
                    self.result.test_failed()
                    self.result.add_failed_case({self.count: msg})
                    Log.logger.info(msg)
        elif style == ActionEnum.Pm:
            log = "[{}] 总计次数: {} 通过次数: {}  非期望次数：{}  原始数据： {}  失败数据为{} ".format(
                case.remark,
                self.result.cycle,
                self.result.pass_count, self.result.error_count,
                self.result.list, self.result.failed_list)
            Log.logger.info(log)
            if len(self.result.list) != 0:
                self.huatu(case.name)
            ts = TestDevice(case.name, case.remark, self.result.cycle, self.result.pass_count,
                            str(case.gw_cmd_list), str(case.nd_cmd_list), sum(self.result.list) / len(self.result.list),
                            self.start_time,
                            self.end_time, case.result, str(self.result.failed_list), str(self.result.error_list))
            TestResult.resultList.update({case.name: ts})
        elif style == ActionEnum.Pe:
            ExportSingle(TestResult.resultList, "res.xlsx")

        else:
            raise ("Error ! Action not Found ")

    def test_handle_multi(self, case, style):
        if style == ActionEnum.Ps:
            if case.result_info is not None:
                self.list = case.result_info.is_exist_multi()
                data = AutoTest.case_data[AutoTest.case_name]
                self.msg = ''
                for i in range(0, len(self.list)):
                    temp_data = self.list[i]
                    if "@NodeList" in temp_data:
                        self.node_list = config.Config.arg['TestCase']['Node_List']
                        for node in self.node_list:
                            self.res = True
                            if len(AutoTest.Start_time_map) > 0:
                                self.start_time = AutoTest.Start_time_map[node]
                            temp_data = temp_data.replace("@NodeList", node)
                            if node not in TestResult.nodeList:
                                temp_node = TestDeviceMul(case.name, node, self.result.cycle, str(case.gw_cmd_list),
                                                          case.timeout, 0)
                                TestResult.nodeList.update({node: temp_node})
                            for i in config.Config.arg['Device']:
                                if ('$' + i) in temp_data:
                                    temp_data = temp_data.replace('$' + i, str(config.Config.arg['Device'][i]))
                            # if temp_data not in data:
                            #     self.msg += 'data {} Not Found'.format(temp_data)
                            #     self.res = False
                            head = re.findall(r'\[.+?(?=:)', temp_data)[0]
                            body = re.findall(r'(?<=:).+', temp_data)[0]
                            # TODO
                            if head not in data:
                                self.msg += 'title {} Not Found'.format(head)
                                self.res = False

                            if '&' not in body:
                                if body not in data:
                                    self.msg += 'MSG: {} Not Found ;'.format(body)
                                    self.res = False
                            else:
                                body_list = re.findall('[^&]+?(?=&|$)', body)
                                for b in body_list:
                                    if b not in data:
                                        self.msg += 'MSG: {} Not Found ;'.format(b)
                                        self.res = False
                            time_list = re.findall('(?<=36;1m\[).+?(?=\])', data)
                            if len(time_list) > 0:
                                self.time = time_list[-1]
                            if self.res:
                                self.end_time = utils.timestr_to_timestamp(self.time) if self.time is not None else None
                                if self.end_time is not None:
                                    use_time = self.end_time  - self.start_time
                                    TestResult.nodeList[node].cal_time(use_time)
                                    self.result.test_pass()
                            temp_data = temp_data.replace(node, "@NodeList")
                    elif "@MacList" in temp_data:
                        self.node_list = config.Config.arg['TestCase']['Node_MAC_List']
                        for node in self.node_list:
                            self.res = True
                            if len(AutoTest.Start_time_map) > 0:
                                self.start_time = AutoTest.Start_time_map[node]
                            temp_data = temp_data.replace("@MacList", node)
                            if node not in TestResult.nodeList:
                                temp_node = TestDeviceMul(case.name, node, self.result.cycle, str(case.gw_cmd_list),
                                                          case.timeout, 0)
                                TestResult.nodeList.update({node: temp_node})
                            for i in config.Config.arg['Device']:
                                if ('$' + i) in temp_data:
                                    temp_data = temp_data.replace('$' + i, str(config.Config.arg['Device'][i]))
                            # if temp_data not in data:
                            #     self.msg += 'data {} Not Found'.format(temp_data)
                            #     self.res = False
                            head = re.findall(r'\[.+?(?=:)', temp_data)[0]
                            body = re.findall(r'(?<=:).+', temp_data)[0]
                            # TODO
                            if head not in data:
                                self.msg += 'title {} Not Found'.format(head)
                                self.res = False

                            if '&' not in body:
                                if body not in data:
                                    self.msg += 'MSG: {} Not Found ;'.format(body)
                                    self.res = False
                            else:
                                body_list = re.findall('[^&]+?(?=&|$)', body)
                                for b in body_list:
                                    if b not in data:
                                        self.msg += 'MSG: {} Not Found ;'.format(b)
                                        self.res = False
                            time_list = re.findall('(?<=36;1m\[).+?(?=\])', data)
                            if len(time_list) > 0:
                                self.time = time_list[-1]
                            if self.res:
                                self.end_time = utils.timestr_to_timestamp(self.time) if self.time is not None else None
                                if self.end_time is not None:
                                    use_time = self.end_time - self.start_time
                                    TestResult.nodeList[node].cal_time(use_time)
                                    self.result.test_pass()
                            temp_data = temp_data.replace(node, "@MacList")
        elif style == ActionEnum.Pm:
            log = "[{}] 总计次数: {} 通过次数: {}  非期望次数：{}  原始数据： {}  失败数据为{} ".format(
                case.remark,
                self.result.cycle,
                self.result.pass_count, self.result.error_count,
                self.result.list, self.result.failed_list)
            Log.logger.info(log)
            # if len(self.result.list) != 0:
            #     self.huatu(case.name)
            # ts = TestDevice(case.name, case.remark, self.result.cycle, self.result.pass_count,
            #                 str(case.gw_cmd_list), str(case.nd_cmd_list), sum(self.result.list) / len(self.result.list),
            #                 self.start_time,
            #                 self.end_time, case.result, str(self.result.failed_list), str(self.result.error_list))
            # TestResult.resultList.update({case.name: ts})
        elif style == ActionEnum.Pe:
            if config.Config.arg['TestCase']['case'] == "multi":
                ExportMulticast(TestResult.case, "MulticastResult.xlsx")

        else:
            raise ("Error ! Action not Found ")

    def open_port(self, port: str, bitrate: int):
        if self.sp is None or self.sp.p is None:
            self.sp = SerialPort(port, bitrate)
            self.sp.open()
        elif self.port != port or self.bitrate != bitrate:
            try:
                self.sp.close()
                self.sp = SerialPort(port, bitrate)
                self.sp.open()
            except Exception as e:
                print(e)
                self.sp = None
        self.port = port
        self.bitrate = bitrate

    def start(self, port: str, bitrate: int, ssh):
        if config.Config.arg['TestCase']['type'] == 'mqtt':
            self.server = mqtt.MqttClient()
            utils.run_with_thread(self.server.start)
        else:
            self.server = ssh
        self.open_port(port, bitrate)
        self.port = port
        self.bitrate = bitrate
        time.sleep(5)
        try:
            for i in range(0, self.cycles):
                for key in self.case.keys():
                    if key is not None:
                        case_list = self.case[key]
                        self.case_list_process(key, case_list)
                self.operation[self.user_case_name]({}, ActionEnum.Pe)
            self.sp.close()
            log = "测试全部结束！！！".center(100, '-')
            Log.logger.info(log)


        except Exception as e:
            Log.logger.error("[Test Get Exception] {}".format(e))

    def case_list_process(self, key, case_list):

        try:
            case_index = 0
            for case in case_list:
                if case.skip is not True:
                    self.result = TestResult(key, case.cycle)
                    for t in range(0, case.cycle):
                        Log.logger.info(
                            "---------------------------------------------------------")
                        log = "[Test Case] Name: {} Total: {} Number: {}".format(case.name, case.cycle, t + 1)
                        Log.logger.info(log)
                        self.count = t + 1
                        case_index += 1
                        utils.run_with_thread(self.test(case))
                        while self.timeout:
                            test_result = {case.name: self.res}
                            self.timeout = False
                    self.result.finish()
                    self.operation[self.user_case_name](case, ActionEnum.Pm)
                    if config.Config.arg['TestCase']['case'] == "multi":
                        TestResult.case.update({case.name: TestResult.nodeList})
                        TestResult.nodeList = {}
        except Exception as e:
            log = "[Test Error] {}".format(e)
            Log.logger.error(log)

    def huatu(self, title):
        pass

    def set_case_sample(self, gw: Dict[str, List[TestCase]], node: Dict[str, List[TestCase]],
                        case: Dict[str, List[TestCase]]):
        self.case = case
        self.gw = gw
        self.node = node

    def __init__(self):
        self.res = None
        self.timeout = False
        self.sp = None
        self.case = None
        self.server = None
        self.case_index_map: Dict[str, int] = {}
        self.test_result: Dict[str, TestResult] = {}
        self.result = None
        self.count = None
        self.start_time = None
        self.end_time = None
        self.device = None
        self.list = None
        self.msg = None
        self.cycles = config.Config.arg['TestCase']['cycle']
        self.user_case_name = config.Config.arg['TestCase']['case']
        self.operation = {
            "performance": self.test_performance,
            "common": self.test_performance,
            "multi": self.test_handle_multi
        }
